﻿using System.IO.Compression;
using System.Security.Cryptography;
using ApiLib.AppStart;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.AspNetCore.Mvc;
using ZipFile = System.IO.Compression.ZipFile;

namespace ApiLib.Utils;

/// <summary>
/// 文件工具类
/// </summary>
public static class FileUtil
{
    /// <summary>
    /// 获取唯一值，Md5方式32位
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static string HashMd5(Stream stream)
    {
        using var md5 = MD5.Create();
        var bytes = md5.ComputeHash(stream);
        return BitConverter.ToString(bytes).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// 获取唯一值，Md5方式32位
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    public static string HashMd5(string filePath)
    {
        using var stream = File.OpenRead(filePath);
        using var md5 = MD5.Create();
        var bytes = md5.ComputeHash(stream);
        /*var sb = new StringBuilder();
        foreach (var b in bytes)
        {
            sb.Append(b.ToString("x2"));
        }

        return sb.ToString();*/
        return BitConverter.ToString(bytes).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// 获取唯一值，Sha256方式65位
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static string HashSha256(Stream stream)
    {
        using var sha256 = SHA256.Create();
        var hash = sha256.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// 获取唯一值，Sha256方式65位
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    public static string HashSha256(string filePath)
    {
        using var stream = File.OpenRead(filePath);
        using var sha256 = SHA256.Create();
        var hash = sha256.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// 获取唯一值，Sha1方式40位
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static string HashSha1(Stream stream)
    {
        using var sha1 = SHA1.Create();
        var hash = sha1.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// 获取唯一值，Sha1方式40位
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    public static string HashSha1(string filePath)
    {
        using var stream = File.OpenRead(filePath);
        using var sha1 = SHA1.Create();
        var hash = sha1.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
    }

    /// <summary>
    /// IO操作
    /// </summary>
    public static class Io
    {
        /// <summary>
        /// 写入文件
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <param name="lines">行</param>
        /// <param name="append">是否拼接，默认false</param>
        public static void WriteText(string path, IEnumerable<string> lines, bool append = false)
        {
            using var writer = new StreamWriter(path, append);
            foreach (var line in lines)
            {
                writer.WriteLine(line);
            }
        }

        /// <summary>
        /// 写入文件
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <param name="content">内容</param>
        /// <param name="writeLine">是否新的行，默认false</param>
        /// <param name="append">是否拼接，默认false</param>
        public static void WriteText(string path, string content, bool writeLine = false, bool append = false)
        {
            using var writer = new StreamWriter(path, append);
            if (writeLine)
            {
                writer.WriteLine(content);
            }
            else
            {
                writer.Write(content);
            }
        }

        /// <summary>
        /// 全部读取
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <returns></returns>
        public static string ReadTextAll(string path)
        {
            using var reader = new StreamReader(path);
            return reader.ReadToEnd();
        }

        /// <summary>
        /// 全部读取，返回所有的行
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <returns></returns>
        public static List<string> ReadTextLines(string path)
        {
            var lines = new List<string>();
            using var reader = new StreamReader(path);
            while (reader.ReadLine() is { } line)
            {
                lines.Add(line);
            }

            return lines;
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="path"></param>
        public static void DeleteFile(string path)
        {
            if (File.Exists(path))
            {
                File.Delete(path);
            }
        }

        /// <summary>
        /// 移动/重命名文件
        /// </summary>
        /// <param name="sourceFilePath">源文件</param>
        /// <param name="destinationFilePath">目标文件</param>
        public static void MoveFile(string sourceFilePath, string destinationFilePath)
        {
            File.Move(sourceFilePath, destinationFilePath);
        }
    }

    /// <summary>
    /// 压缩工具
    /// </summary>
    public static class ZipUtil
    {
        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="zipFile"></param>
        /// <param name="filesOrDirs"></param>
        /// <param name="createNew"></param>
        public static void ZipFast(string zipFile, List<string> filesOrDirs, bool createNew = true)
        {
            using var archive = ZipFile.Open(zipFile, createNew ? ZipArchiveMode.Create : ZipArchiveMode.Update);
            foreach (var fileOrDir in filesOrDirs)
            {
                var entry = archive.CreateEntry(fileOrDir, CompressionLevel.Fastest);
                if (!fileOrDir.EndsWith("/"))
                {
                    var stream = entry.Open();
                    using var fileStream = new FileStream(fileOrDir, FileMode.Create);
                    fileStream.CopyTo(stream);
                }
            }
        }

        /// <summary>
        /// 解压文件方法
        /// </summary>
        /// <param name="zipFile">zip文件地址</param>
        /// <param name="extractPath">解压缩到目录</param>
        public static void UnzipFast(string zipFile, string extractPath)
        {
            // 打开zip文件
            using var archive = ZipFile.OpenRead(zipFile);
            // 遍历zip文件中的每个文件
            foreach (var entry in archive.Entries)
            {
                // 创建文件保存路径
                var fullPath = Path.Combine(extractPath, entry.FullName);
                // 创建文件夹
                if (entry.FullName.EndsWith("/"))
                {
                    Directory.CreateDirectory(fullPath);
                    continue;
                }

                // 创建文件流
                using var fileStream = new FileStream(fullPath, FileMode.Create);
                // 复制zip文件的流到文件流中
                using var stream = entry.Open();
                stream.CopyTo(fileStream);
            }
        }

        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="fileInfos"></param>
        /// <param name="useUnicode">如果Linux下出现乱码，尝试设置为true</param>
        /// <returns></returns>
        public static MemoryStream Zip(List<FileInfo> fileInfos, bool useUnicode = false)
        {
            if (useUnicode)
            {
                ZipStrings.UseUnicode = true;
            }

            var outputMemStream = new MemoryStream();
            using var zipStream = new ZipOutputStream(outputMemStream);
            zipStream.SetLevel(5); //0-9, 9 being the highest level of compression
            foreach (var fileInfo in fileInfos)
            {
                var newEntry = new ZipEntry(fileInfo.Name);
                zipStream.PutNextEntry(newEntry);
                using var stream = fileInfo.OpenRead();
                StreamUtils.Copy(stream, zipStream, new byte[4096]);
            }

            return outputMemStream;
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="zipFile"></param>
        /// <param name="dir"></param>
        public static void Compress(string zipFile, string dir)
        {
            var fileInfo = new FileInfo(dir);
            if (!string.IsNullOrEmpty(fileInfo.DirectoryName))
            {
                Directory.CreateDirectory(fileInfo.DirectoryName);
            }

            using var zipOutputStream = new ZipOutputStream(File.Create(zipFile));
            zipOutputStream.SetLevel(6);
            CompressDir(zipOutputStream, dir);
            zipOutputStream.Finish();
            zipOutputStream.Close();
        }

        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="source">源目录</param>
        /// <param name="zipOutput">ZipOutputStream对象</param>
        public static void CompressDir(ZipOutputStream zipOutput, string source)
        {
            var filenames = Directory.GetFileSystemEntries(source);
            foreach (var file in filenames)
            {
                if (Directory.Exists(file))
                {
                    CompressDir(zipOutput, file); //递归压缩子文件夹
                }
                else
                {
                    CompressFile(zipOutput, file);
                }
            }
        }

        /// <summary>
        /// 压缩文件
        /// </summary>
        /// <param name="file">文件</param>
        /// <param name="zipOutput">ZipOutputStream对象</param>
        public static void CompressFile(ZipOutputStream zipOutput, string file)
        {
            using var fs = File.OpenRead(file);
            var buffer = new byte[4 * 1024];
            var entry = new ZipEntry(file.Replace((Path.GetPathRoot(file) ?? ""), ""))
            {
                DateTime = DateTime.Now
            }; //此处去掉盘符，如D:\123\1.txt 去掉D:
            zipOutput.PutNextEntry(entry);

            int sourceBytes;
            do
            {
                sourceBytes = fs.Read(buffer, 0, buffer.Length);
                zipOutput.Write(buffer, 0, sourceBytes);
            } while (sourceBytes > 0);
        }

        /// <summary>
        /// 解压缩
        /// </summary>
        /// <param name="sourceFile">源文件</param>
        /// <param name="targetPath">目标路经</param>
        public static bool Decompress(string sourceFile, string targetPath)
        {
            if (!File.Exists(sourceFile))
            {
                throw new FileNotFoundException($"未能找到文件 '{sourceFile}' ");
            }

            if (!Directory.Exists(targetPath))
            {
                Directory.CreateDirectory(targetPath);
            }

            using var s = new ZipInputStream(File.OpenRead(sourceFile));
            while (s.GetNextEntry() is { } theEntry)
            {
                var directorName = Path.Combine(targetPath, (Path.GetDirectoryName(theEntry.Name) ?? ""));
                var fileName = Path.Combine(directorName, Path.GetFileName(theEntry.Name));
                // 创建目录
                if (directorName.Length > 0)
                {
                    Directory.CreateDirectory(directorName);
                }

                if (fileName != string.Empty)
                {
                    using var streamWriter = File.Create(fileName);
                    var data = new byte[4 * 1024];
                    while (true)
                    {
                        var size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else break;
                    }
                }
            }

            return true;
        }
    }

    /// <summary>
    /// 压缩工具
    /// </summary>
    public static class CompressionUtil
    {
        /// <summary>
        /// Deflate
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string DeflateCompress(string data)
        {
            if (string.IsNullOrEmpty(data))
            {
                return "";
            }

            var bytes = DeflateCompress(data.GetBytes());
            return bytes?.GetString() ?? "";
        }

        /// <summary>
        /// Deflate
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[]? DeflateCompress(byte[]? data)
        {
            if (data == null || data.Length < 1)
                return data;
            try
            {
                using var stream = new MemoryStream();
                using var gZipStream = new DeflateStream(stream, CompressionMode.Compress);
                gZipStream.Write(data, 0, data.Length);
                gZipStream.Close();
                return stream.ToArray();
            }
            catch (Exception)
            {
                return data;
            }
        }

        /// <summary>
        /// Gzip
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string GzipCompress(string data)
        {
            if (string.IsNullOrEmpty(data))
            {
                return "";
            }

            var bytes = GzipCompress(data.GetBytes());
            return bytes?.GetString() ?? "";
        }

        /// <summary>
        /// Gzip
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[]? GzipCompress(byte[]? data)
        {
            if (data == null || data.Length < 1)
                return data;
            try
            {
                using var stream = new MemoryStream();
                using var gZipStream = new GZipStream(stream, CompressionMode.Compress);
                gZipStream.Write(data, 0, data.Length);
                gZipStream.Close();
                return stream.ToArray();
            }
            catch (Exception)
            {
                return data;
            }
        }
    }
}

/// <summary>
/// 文件扩展
/// </summary>
public static class FileExtend
{
    /// <summary>
    /// 获取文件结果，用于返回文件
    /// </summary>
    /// <param name="stream">流</param>
    /// <param name="fileName">文件名</param>
    /// <param name="contentType">文件类型</param>
    /// <returns></returns>
    public static FileResult ToExport(this Stream stream, string fileName,
        string contentType = GlobalConstants.FileContentType.Normal)
    {
        FileResult fr = stream is MemoryStream ms
            ? new FileContentResult(ms.ToArray(), contentType)
            {
                FileDownloadName = fileName
            }
            : new FileStreamResult(stream, contentType)
            {
                FileDownloadName = fileName
            };

        stream.Dispose();
        return fr;
    }

    /// <summary>
    /// 扫描文件夹
    /// </summary>
    /// <param name="dir"></param>
    /// <param name="action"></param>
    /// <param name="action2"></param>
    public static void ScanDir(this DirectoryInfo dir, Action<FileInfo[]> action,
        Action<DirectoryInfo[]>? action2 = null)
    {
        action.Invoke(dir.GetFiles());
        var directories = dir.GetDirectories();
        action2?.Invoke(directories);
        foreach (var directory in directories)
        {
            ScanDir(directory, action);
        }
    }
}